/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.aplikator.server.persistence.empiredb.oracle;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.empire.data.DataMode;
import org.apache.empire.data.DataType;
import org.apache.empire.db.DBCmdType;
import org.apache.empire.db.DBColumn;
import org.apache.empire.db.DBDatabase;
import org.apache.empire.db.DBIndex;
import org.apache.empire.db.DBRelation;
import org.apache.empire.db.DBRelation.DBReference;
import org.apache.empire.db.DBSQLScript;
import org.apache.empire.db.DBTable;
import org.apache.empire.db.DBTableColumn;
import org.apache.empire.db.DBView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EmpireDataDictionnaryOracle {

	/**
	 * Immutable column info helper class
	 */
	public class ColumnInfo {

		private final String dataType;
		private final int charLength;
		private final int dataLength;
		private final int dataPrecision;
		private final int dataScale;
		private final String nullable;
		private boolean processed = false;

		public ColumnInfo(String dataType, int charLength, int dataLength,
				int dataPrecision, int dataScale, String nullable) {
			super();
			this.dataType = dataType;
			this.charLength = charLength;
			this.dataLength = dataLength;
			this.dataPrecision = dataPrecision;
			this.dataScale = dataScale;
			this.nullable = nullable;
		}

		public int getDataLength() {
			return dataLength;
		}

		public int getDataPrecision() {
			return dataPrecision;
		}

		public int getDataScale() {
			return dataScale;
		}

		public String getDataType() {
			return dataType;
		}

		public int getCharLength() {
			return charLength;
		}

		public String getNullable() {
			return nullable;
		}

		public boolean isProcessed() {
			return processed;
		}

		public void setProcessed(boolean processed) {
			this.processed = processed;
		}

	}

	public class ConstraintColumns {
		private final String tableName;
		private final String columnName;
		private final Integer position;
		private boolean processed = false;

		public ConstraintColumns(String tableName, String columnName,
				Integer position) {
			super();
			this.tableName = tableName;
			this.columnName = columnName;
			this.position = position;
		}

		public String getColumnName() {
			return columnName;
		}

		public Integer getPosition() {
			return position;
		}

		public String getTableName() {
			return tableName;
		}

		public boolean isProcessed() {
			return processed;
		}

		public void setProcessed(boolean processed) {
			this.processed = processed;
		}
	}
	
	public class IndexInfo {
		private final String indexName;
		private final String tableName;
		private final boolean unique;
		private boolean processed;
		public boolean isProcessed() {
			return processed;
		}

		public boolean isUnique() {
			return unique;
		}

		public void setProcessed(boolean processed) {
			this.processed = processed;
		}



		public String getTableName() {
			return tableName;
		}

		private final HashMap<String, ConstraintColumns> indexColumns = new HashMap<String, ConstraintColumns>();
		
		public String getIndexName() {
			return indexName;
		}
		
		

		public HashMap<String, ConstraintColumns> getIndexColumns() {
			return indexColumns;
		}
		
		public IndexInfo(String indexName, String tableName, boolean unique) {
			this.indexName=indexName;
			this.tableName=tableName;
			this.unique=unique;
		}		
	}

	public class ConstraintInfo {
		private final String constraintName;
		private final String tableName;
		private final String rConstraintName;
		private final String constraintType;
		private final String status;
		private boolean processed = false;

		private final HashMap<String, ConstraintColumns> constraintColumns = new HashMap<String, ConstraintColumns>();

		public ConstraintInfo(String constraintName, String tableName,
				String rConstraintName, String constraintType, String status) {
			super();
			this.constraintName = constraintName;
			this.tableName = tableName;
			this.rConstraintName = rConstraintName;
			this.constraintType = constraintType;
			this.status = status;
		}

		public HashMap<String, ConstraintColumns> getConstraintColumns() {
			return constraintColumns;
		}

		public String getConstraintName() {
			return constraintName;
		}

		public String getConstraintType() {
			return constraintType;
		}

		public String getrConstraintName() {
			return rConstraintName;
		}

		public String getStatus() {
			return status;
		}

		public String getTableName() {
			return tableName;
		}

		public boolean isProcessed() {
			return processed;
		}

		public void setProcessed(boolean processed) {
			this.processed = processed;
		}
	}

	public class Sequences {
		private final String sequenceName;
		private boolean processed = false;

		public Sequences(String sequenceName) {
			super();
			this.sequenceName = sequenceName;
		}

		public String getSequenceName() {
			return sequenceName;
		}

		public boolean isProcessed() {
			return processed;
		}

		public void setProcessed(boolean processed) {
			this.processed = processed;
		}

	}

	protected static final Logger log = LoggerFactory
			.getLogger(EmpireDataDictionnaryOracle.class);

	private final HashMap<String, HashMap<String, ColumnInfo>> dictionnary = new HashMap<String, HashMap<String, ColumnInfo>>();

	private final List<String> processedTables = new ArrayList<String>();
	private final List<String> createdTables = new ArrayList<String>();

	private Map<String, DataType[]> dataTypeMapping = null;

	private final DBSQLScript script;

	private final EmpireDDLGeneratorOracle ddlGenerator;

	private final HashMap<String, ConstraintInfo> constraintDictionnary = new HashMap<String, ConstraintInfo>();

	
	/*
	 * Checks a DBTableColumn definition. The set attributes must fint to the
	 * overgiven db attributes.
	 * 
	 * @param col Column to check
	 * 
	 * @param dbDataType Datatype of db column
	 * 
	 * @param dbDataLength Datalength of db column
	 * 
	 * @param dbDataPrecision Data precision of db column
	 * 
	 * @param dbDataScale Data scale of db column
	 * 
	 * @param dbRequired Is nullable of the db column
	 * 
	 * @return true if the column definition fits, false otherwise
	 * 
	 * private boolean checkColumnDefinition(DBColumn col, String dbDataType,
	 * int dbDataLength, int dbDataPrecision, int dbDataScale, boolean
	 * dbRequired) { // FUTURE find a way to check the precision for numbers
	 * 
	 * boolean result = true; DataType colDataType = col.getDataType(); int size
	 * = (int) col.getSize();
	 * 
	 * // check if the column data type can be mapped with the db column data
	 * type if (checkMapping(dbDataType, colDataType) == false) {
	 * log.warn("WRONG DATATYPE \t\t\t\t\t\t: [" + col.getRowSet().getName() +
	 * "][" + col.getName() + "] -> DB : [" + dbDataType + "]" + "[" +
	 * dbDataLength + "]"); result = false; }
	 * 
	 * // check if the column is required and if the column is defined as
	 * required if (dbRequired && col.isRequired() == false) {
	 * log.warn("COLUMN IS REQUIRED \t\t\t\t\t: [" + col.getRowSet().getName() +
	 * "][" + col.getName() + "]"); result = false; } else if (dbRequired ==
	 * false && col.isRequired() == true) {
	 * log.warn("COLUMN IS NOT REQUIRED \t\t\t\t: [" + col.getRowSet().getName()
	 * + "][" + col.getName() + "]"); result = false; }
	 * 
	 * // check the data length if the column is a varchar2 if
	 * (dbDataType.equals("VARCHAR2") && (dbDataLength != size)) {
	 * log.warn("WRONG COLUMN SIZE \t\t\t\t\t: [" + col.getRowSet().getName() +
	 * "][" + col.getName() + "] -> DB : [" + dbDataType + "][" + dbDataLength +
	 * "]"); result = false; }
	 * 
	 * return result; }
	 */

	private final HashMap<String, Sequences> sequencesDictionary = new HashMap<String, Sequences>();

	private final HashMap<String, IndexInfo> indexesDictionary = new HashMap<String, IndexInfo>();
	
	/**
	 * Defines mapping of the Empire-db data types with the oracle data types.
	 */
	public EmpireDataDictionnaryOracle(DBSQLScript script,
			EmpireDDLGeneratorOracle ddlGenerator) {
		this.script = script;
		this.ddlGenerator = ddlGenerator;
		dataTypeMapping = new HashMap<String, DataType[]>();
		dataTypeMapping.put("VARCHAR2", new DataType[] { DataType.TEXT });
		dataTypeMapping.put("CHAR", new DataType[] { DataType.CHAR,
				DataType.BOOL });
		dataTypeMapping.put("NUMBER", new DataType[] { DataType.DECIMAL,
				DataType.FLOAT, DataType.INTEGER, DataType.AUTOINC,
				DataType.BOOL });
		dataTypeMapping.put("DATE", new DataType[] { DataType.DATE });
		dataTypeMapping.put("CLOB", new DataType[] { DataType.CLOB });
		dataTypeMapping.put("BLOB", new DataType[] { DataType.BLOB });
		dataTypeMapping.put("FLOAT", new DataType[] { DataType.FLOAT });
		dataTypeMapping.put("TIMESTAMP(6)",
				new DataType[] { DataType.DATETIME });
	}

	public void fillConstraintColumns(String constraintName, String tableName,
			String columnName, Integer position) {
		ConstraintInfo constraintInfo = constraintDictionnary
				.get(constraintName);
		if (constraintInfo != null) {
			constraintInfo.getConstraintColumns().put(columnName,
					new ConstraintColumns(tableName, columnName, position));
		} else {
			log.warn("Constraint \t+" + constraintName + " not defined");
		}
	}

	public void fillIndexColumns(String indexName, String tableName, String columnName, Integer position) {
		IndexInfo indexInfo = indexesDictionary
				.get(indexName.toUpperCase());
		if (indexInfo != null) {
			indexInfo.getIndexColumns().put(columnName.toUpperCase(),
					new ConstraintColumns(tableName, columnName, position));
		} else {
			log.warn("Index \t+" + indexName + " not defined");
		}
	}

	
	public void fillConstraints(String constraintName, String tableName,
			String rConstraintName, String constraintType, String status) {
		constraintDictionnary.put(constraintName, new ConstraintInfo(
				constraintName, tableName, rConstraintName, constraintType,
				status));
	}

	public void fillDataDictionnary(String tableName, String columnName,
			String dataType, int charLength, int dataLength, int dataPrecision,
			int dataScale, String nullable) {
		ColumnInfo colInfo = new ColumnInfo(dataType, charLength, dataLength,
				dataPrecision, dataScale, nullable);
		HashMap<String, ColumnInfo> columns = new HashMap<String, ColumnInfo>();

		if (dictionnary.containsKey(tableName)) {
			columns = dictionnary.get(tableName);
		}
		columns.put(columnName, colInfo);
		dictionnary.put(tableName, columns);
	}

	public void fillSequences(String sequenceName) {
		sequencesDictionary.put(sequenceName, new Sequences(sequenceName));
	}
	
	public void fillIndexes(String indexName, String tableName, boolean unique) {
		indexesDictionary.put(indexName.toUpperCase(), new IndexInfo(indexName.toUpperCase(),tableName,unique));		
	}

	public HashMap<String, HashMap<String, ColumnInfo>> getDictionnary() {
		return dictionnary;
	}

	private void checkColumn(DBColumn column, ColumnInfo colInfo) {
		if (checkMapping(colInfo.getDataType(), column.getDataType()) == false) {
			log.warn("WRONG DATA TYPE: \t" + column.getFullName()
					+ " is set to " + column.getDataType() + " instead of "
					+ colInfo.getDataType());
			ddlGenerator.getDDLScript(DBCmdType.ALTER, column, script);
		}
		if (colInfo.getDataType().equals("VARCHAR2")
				&& (column.getSize() != colInfo.getCharLength())) {
			log.warn("WRONG COLUMN SIZE: \t" + column.getFullName()
					+ " is set to " + (int) column.getSize() + " instead of "
					+ colInfo.getCharLength());
			ddlGenerator.getDDLScript(DBCmdType.ALTER, column, script);
		}
		if ((column.isRequired() || column.isAutoGenerated()) != colInfo
				.getNullable().equals("N")) {
			log.warn("WRONG NULLABLE FLAG: \t" + column.getFullName()
					+ " is set to " + column.isRequired() + " instead of "
					+ colInfo.getNullable().equals("N"));
			ddlGenerator.changeNullabilityOnly((DBTableColumn) column, script);
		}
	}

	public void checkDBConstraints(DBDatabase db) {
		List<DBTable> dbTables = db.getTables();
		for (DBTable currentTable : dbTables) {
			if (createdTables.contains(currentTable.getName())) {
				continue;
			}
			DBIndex primaryKey = currentTable.getPrimaryKey();
			if (primaryKey == null) {
				// no primary key defined
				continue;
			}
			String pkName = primaryKey.getName();
			ConstraintInfo constraint = constraintDictionnary.get(pkName
					.toUpperCase());
			if (constraint == null) {
				log.warn("Primary key \t" + pkName + " is missing");
				ddlGenerator.getDDLScript(DBCmdType.CREATE, primaryKey, script);
				continue;
			}

			constraint.setProcessed(true);

			if (!constraint.getConstraintType().trim().equalsIgnoreCase("P")) {
				log.warn("Primary key \t" + pkName + " type is wrong:"
						+ constraint.getConstraintType());
				ddlGenerator.getDDLScript(DBCmdType.ALTER, primaryKey, script);
				continue;
			}

			HashMap<String, ConstraintColumns> pkColumns = constraint
					.getConstraintColumns();

			if (primaryKey.getColumns().length != pkColumns.size()) {
				log.warn("Primary key \t" + pkName
						+ " wrong number of columns ");
				ddlGenerator.getDDLScript(DBCmdType.ALTER, primaryKey, script);
			}

			for (int columnPos = 0; columnPos < primaryKey.getColumns().length; columnPos++) {
				DBColumn dbColumn = primaryKey.getColumns()[columnPos];
				ConstraintColumns column = pkColumns.get(dbColumn.getName()
						.toUpperCase());
				if (column == null) {
					log.warn("Primary key \t" + pkName + "  column "
							+ dbColumn.getName() + " missing");
					ddlGenerator.getDDLScript(DBCmdType.ALTER, primaryKey,
							script);
					continue;
				}
				if ((columnPos + 1) != column.getPosition()) {
					log.warn("Primary key \t" + pkName
							+ " wrong position of column " + dbColumn.getName());
					ddlGenerator.getDDLScript(DBCmdType.ALTER, primaryKey,
							script);
					continue;
				}

			}
		}

		// Check relations
		List<DBRelation> dbRelations = db.getRelations();
		for (DBRelation currentRelation : dbRelations) {
			String relationName = currentRelation.getName();
			ConstraintInfo constraint = constraintDictionnary.get(relationName
					.toUpperCase());
			if (constraint == null) {
				log.warn("Foreign key constraint \t" + relationName
						+ " missing");
				ddlGenerator.getDDLScript(DBCmdType.CREATE, currentRelation,
						script);
				continue;
			}
			constraint.setProcessed(true);

			if (!constraint.getConstraintType().trim().equalsIgnoreCase("R")) {
				log.warn("Foreign key constraint \t" + relationName
						+ " wrong type:" + constraint.getConstraintType());
				ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
						script);
			}

			if (!currentRelation.getForeignKeyTable().getName()
					.equalsIgnoreCase(constraint.tableName)) {
				log.warn("Foreign key constraint \t" + relationName
						+ " table is wrong:"
						+ currentRelation.getForeignKeyTable().getName());
				ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
						script);
				continue;
			}

			DBReference[] references = currentRelation.getReferences();
			if (references.length != constraint.getConstraintColumns().size()) {
				log.warn("Foreign key \t" + relationName
						+ " wrong number of columns ");
				ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
						script);
			}

			ConstraintInfo primaryKeyTarget = constraintDictionnary
					.get(constraint.getrConstraintName().toUpperCase());
			if (primaryKeyTarget == null) {
				log.warn("Foreign key constraint \t" + relationName
						+ " primary key constraint missing:"
						+ constraint.getrConstraintName());
				ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
						script);
			}

			for (int columnPos = 0; columnPos < references.length; columnPos++) {
				DBReference reference = references[columnPos];
				String sourceTable = reference.getSourceColumn().getRowSet()
						.getName();
				String sourceColumn = reference.getSourceColumn().getName();
				String targetTable = reference.getTargetColumn().getRowSet()
						.getName();
				String targetColumn = reference.getTargetColumn().getName();
				ConstraintColumns column = constraint.getConstraintColumns()
						.get(sourceColumn.toUpperCase());
				ConstraintColumns pkColumn = primaryKeyTarget
						.getConstraintColumns().get(targetColumn.toUpperCase());
				if (column == null) {
					log.warn("Foreign key \t" + relationName
							+ " is missing source column " + sourceColumn);
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
							script);
					continue;
				}

				if (column.getPosition() != (columnPos + 1)) {
					log.warn("Foreign key \t" + relationName
							+ " source column " + sourceColumn
							+ " has wrong position");
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
							script);
				}

				if (!constraint.getTableName().equalsIgnoreCase(sourceTable)) {
					log.warn("Foreign key \t" + relationName
							+ " source table is wrong");
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
							script);
				}

				if (!pkColumn.getTableName().equalsIgnoreCase(targetTable)) {
					log.warn("Foreign key \t" + relationName + " target TABLE "
							+ targetTable + " does not match "
							+ pkColumn.getTableName());
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
							script);
				}

				if (pkColumn.getPosition() != (columnPos + 1)) {
					log.warn("Foreign key \t" + relationName
							+ " target column in PK "
							+ pkColumn.getColumnName() + " has wrong position");
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentRelation,
							script);
				}

			}
		}
	}

	public void checkDBSequences(DBDatabase db) {
		List<DBTable> dbTables = db.getTables();
		for (DBTable currentTable : dbTables) {
			List<DBColumn> dbColumns = currentTable.getColumns();
			for (DBColumn currentColumn : dbColumns) {
				if (currentColumn.isAutoGenerated()) {
					String seqName = ((DBTableColumn) currentColumn)
							.getSequenceName().toUpperCase();
					if (seqName != null) {
						Sequences sequence = sequencesDictionary.get(seqName);
						if (sequence == null) {
							log.warn("Sequence " + seqName + " is missing");
							ddlGenerator.createSequence(db,
									(DBTableColumn) currentColumn, script);
						} else {
							sequence.setProcessed(true);
						}
					}
				}
			}
		}
	}
	
	private void checkDBIndexDefinition(DBTable table) {
		List<DBIndex> tableIndexes = table.getIndexes();
		
		for (DBIndex currentIndex : tableIndexes) {
			boolean skipColumnsCheck=false;

			IndexInfo indexInfoDict = this.indexesDictionary.get(currentIndex.getName().toUpperCase());
			if (indexInfoDict==null) {
				log.debug("Index "+currentIndex.getName()+" not found");
				if ((currentIndex.getType()==DBIndex.PRIMARYKEY)&&(createdTables.contains(table.getName()))) {
					log.debug("Primary key "+currentIndex.getName()+" skipped - already created");
				} else {
					ddlGenerator.getDDLScript(DBCmdType.CREATE, currentIndex, script);
				}
				continue;				
			}
			
			
			indexInfoDict.setProcessed(true);
			
			boolean currentIndexUnique=false;
			if ((currentIndex.getType()==DBIndex.UNIQUE)||(currentIndex.getType()==DBIndex.PRIMARYKEY)) {
				currentIndexUnique=true;
			}
			
			if (currentIndexUnique!=indexInfoDict.unique) {
				log.debug("Index "+currentIndex.getName()+" uniqueness changed");
				ddlGenerator.getDDLScript(DBCmdType.ALTER, currentIndex, script);
				skipColumnsCheck=true;
			}
			
			HashMap<String, ConstraintColumns> indexColumnsDict = indexInfoDict.getIndexColumns();
			
			for (DBColumn col : currentIndex.getColumns()) {
				ConstraintColumns columnDict  = indexColumnsDict.get(col.getName().toUpperCase());
				if (columnDict==null) {
					if (skipColumnsCheck) {
						continue;
					}
					// column does not exist in db
					log.debug("Column "+col.getName()+" does not exist in db");
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentIndex, script);
					skipColumnsCheck=true;
					continue;
				}
				columnDict.setProcessed(true);
				if (skipColumnsCheck) {
					// index is rebuilt anyway - just marking columns as processed without checking
					continue;
				}
				if (!columnDict.getPosition().equals(currentIndex.getColumnPos(col)+1)) {
					ddlGenerator.getDDLScript(DBCmdType.ALTER, currentIndex, script);
					skipColumnsCheck=true;
				}
			}			
		}
		
	}
	
	public void checkDBTableDefinition(List<DBTable> dbTables) {
		// go through all tables defined in the Java code
		for (DBTable currentTable : dbTables) {
			String dbTableName = currentTable.getName().toUpperCase();
			// check if the table name is in the data dictionnary
			if (this.dictionnary.containsKey(dbTableName)) {
				processedTables.add(dbTableName);
				// go through all columns of the table
				for (DBColumn currentColumn : currentTable.getColumns()) {
					Collection<String> dictColumns = this.dictionnary.get(
							dbTableName).keySet();
					// check if the column name is in the data dictionnary
					if (dictColumns.contains(currentColumn.getName()
							.toUpperCase())) {
						// go through all columns of the table in the data
						// dictionnary
						for (String dictColumn : dictColumns) {
							// compare the columnnames
							if (currentColumn.getName().equalsIgnoreCase(dictColumn)) {
								ColumnInfo colInfo = this.dictionnary.get(
										currentTable.getName().toUpperCase())
										.get(dictColumn);
								colInfo.setProcessed(true);
								checkColumn(currentColumn, colInfo);
							}
						}
					} else {
						log.warn("MISSING COLUMN: \t"
								+ currentColumn.getFullName()
								+ " does not exist in database");
						ddlGenerator.getDDLScript(DBCmdType.CREATE,
								currentColumn, script);
					}
				}
			} else {
				log.warn("MISSING TABLE: \t" + currentTable.getName()
						+ " does not exist in database");
				ddlGenerator.getDDLScript(DBCmdType.CREATE, currentTable,
						script);
				createdTables.add(currentTable.getName());
			}
			
			if (createdTables.contains(currentTable.getName())) {
			   log.debug("Table "+currentTable+" created already with indexes");
			} else
				checkDBIndexDefinition(currentTable);
			}
	}

	public void checkDBViewDefinition(List<DBView> dbViews) {
		// go through all views defined in the Java code
		for (DBView currentView : dbViews) {
			String dbviewName = currentView.getName();
			// check if the view name is in the data dictionnary
			if (this.dictionnary.containsKey(dbviewName)) {
				processedTables.add(dbviewName);
				// go through all columns of the table
				for (DBColumn currentColumn : currentView.getColumns()) {
					Collection<String> dictColumns = this.dictionnary.get(
							dbviewName).keySet();
					// check if the column name is in the data dictionnary
					if (dictColumns.contains(currentColumn.getName())) {
						// go through all columns of the view in the data
						// dictionnary
						for (String dictColumn : dictColumns) {
							// compare the columnnames
							if (currentColumn.getName().equals(dictColumn)) {
								ColumnInfo colInfo = this.dictionnary.get(
										currentView.getName()).get(dictColumn);
								colInfo.setProcessed(true);
								if (checkMapping(colInfo.getDataType(),
										currentColumn.getDataType()) == false) {
									log.warn("WRONG DATA TYPE: \t"
											+ currentColumn.getFullName()
											+ " is set to "
											+ currentColumn.getDataType()
											+ " instead of "
											+ colInfo.getDataType());
									ddlGenerator.getDDLScript(DBCmdType.ALTER,
											currentColumn, script);
								}
							}
						}
					} else {
						log.warn("MISSING COLUMN: \t"
								+ currentColumn.getFullName()
								+ " does not exist in database.");
						ddlGenerator.getDDLScript(DBCmdType.CREATE,
								currentColumn, script);
					}
				}
			} else {
				log.warn("MISSING VIEW: \t" + currentView.getName()
						+ " does not exist in database.");
				ddlGenerator
						.getDDLScript(DBCmdType.CREATE, currentView, script);
			}
		}
	}

	/**
	 * Checks if the ORACLE datatype can be mapped with a DBTable data type.
	 * 
	 * @param dbDatatype
	 *            ORACLE datatype
	 * @param colDataType
	 *            DBTableColumn datatype
	 * @return true if the type can be mapped, false otherwise
	 */
	private boolean checkMapping(String dbDatatype, DataType colDataType) {
		DataType[] colTypes = dataTypeMapping.get(dbDatatype);
		if (colTypes == null) {
			log.warn("MAPPING NOT DEFINED FOR " + dbDatatype + " -> "
					+ colDataType);
			return false;
		}
		for (int i = 0; i < colTypes.length; i++) {
			if (colTypes[i] == colDataType)
				return true;
		}
		return false;
	}

	public void checkNonUsedObjects(DBDatabase db) {
		for (String currentTable : dictionnary.keySet()) {
			if (!processedTables.contains(currentTable)) {
				log.warn("Table " + currentTable
						+ " not found in database definition");
				DBTable droppedTable = new DBTable(currentTable, db);
				ddlGenerator.getDDLScript(DBCmdType.DROP, droppedTable, script);
				db.getTables().remove(droppedTable);
			}
		}

		for (DBTable currentTable : db.getTables()) {
			HashMap<String, ColumnInfo> columns = dictionnary.get(currentTable
					.getName().toUpperCase());
			if (columns == null) {
				log.warn("No columns for table " + currentTable.getName());
				continue;
			}
			for (String currentColumn : columns.keySet()) {
				if (!columns.get(currentColumn).isProcessed()) {
					DBColumn droppedColumn = new DBTableColumn(currentTable,
							DataType.UNKNOWN, currentColumn, 10,
							DataMode.Nullable, null);
					ddlGenerator.getDDLScript(DBCmdType.DROP, droppedColumn,
							script);
					currentTable.getColumns().remove(droppedColumn);
				}
			}
		}
		
		for (String currentIndex : indexesDictionary.keySet()) {
			if (!indexesDictionary.get(currentIndex.toUpperCase()).isProcessed()) {
				DBIndex droppedIndex = new DBIndex(currentIndex, DBIndex.STANDARD, null);
				
				ddlGenerator.getDDLScript(DBCmdType.DROP, droppedIndex, script);
			} else {
				HashMap<String, ConstraintColumns> indexColumns = indexesDictionary.get(currentIndex).indexColumns; 
				for (String currentColumn : indexColumns.keySet()) {
					if (!indexColumns.get(currentColumn.toUpperCase()).isProcessed()) {
						List<DBIndex> aplikatorIndexes = db.getTable(indexesDictionary.get(currentIndex.toUpperCase()).getTableName()).getIndexes();
						DBIndex aplikatorIndex = null;
						for (DBIndex aplikatorIndexTemp : aplikatorIndexes ) {
							if (aplikatorIndexTemp.getName().equalsIgnoreCase(currentIndex)) {
								aplikatorIndex=aplikatorIndexTemp;
								// index found
								break;
							}
						}
						if (aplikatorIndex==null) {
							log.error("Index "+currentIndex+" not defined");
							throw new IllegalStateException("Index "+currentIndex+" not defined");
						}
						ddlGenerator.getDDLScript(DBCmdType.ALTER, aplikatorIndex, script);
						//index recreated - no need to go through other columns
						break;
					}
				}
			}
		}

		for (String currentSequence : sequencesDictionary.keySet()) {
			if (!sequencesDictionary.get(currentSequence).isProcessed()) {
				ddlGenerator.dropSequence(currentSequence, script);
			}
		}

		for (String currentConstraint : constraintDictionnary.keySet()) {
			ConstraintInfo constraintInfo = constraintDictionnary
					.get(currentConstraint);

			if (!constraintInfo.isProcessed()) {
				DBTable dbTable = db.getTable(constraintInfo.getTableName());
				if (dbTable == null) {
					log.warn("Table " + constraintInfo.getTableName()
							+ " does not exist");
					continue;
				}

				if ("P".equalsIgnoreCase(constraintInfo.getConstraintType()
						.trim())) {

					DBIndex primaryKey = new DBIndex(
							constraintInfo.getConstraintName(),
							DBIndex.PRIMARYKEY, null);
					dbTable.addIndex(primaryKey);
					ddlGenerator.getDDLScript(DBCmdType.DROP, primaryKey,
							script);
					dbTable.getIndexes().remove(primaryKey);
				}

				if ("R".equalsIgnoreCase(constraintInfo.getConstraintType()
						.trim())) {
					ddlGenerator.getDDLDropRelation(constraintInfo.tableName,
							constraintInfo.constraintName, script);
				}
			}
		}
	}

}
